package edu.kit.ksri.db.mturk.plugins.correctness.answers;

import java.util.HashMap;

/**
 * Copyright (c) 2010-2011 Karlsruhe Institute of Technology (KIT), Germany
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 * 
 * --------------------------------------------------------------------------
 * 
 * Software: CSP/WMV tool for dynamic quality management of microtasks
 * http://www.ksri.kit.edu/Upload/Users/PeopleClouds/HCOMP2011_An_extendable_toolkit_for_managing_quality_of_human-based_eServices.pdf
 * 
 * Organization: Karlsruhe Institute of Technology (KIT), Germany
 * http://www.kit.edu
 * 
 * Project: PeopleClouds project of the Karlsruhe Service Research Institute(KSRI)
 * http://www.ksri.kit.edu/Default.aspx?PageId=818
 * 
 * Contributing authors: David Bermbach, Sandra Rath, Pascal Wichmann, Robert Kern
 */
public final class BasicAssignmentAnswer extends AbstractAssignmentAnswer {

	/** defines which of the basic comparison modes shall be used */
	private int mode;

	/**
	 * used in mode IGNORE_NON_PATTERN_ANSWERS, default pattern is "(\\w)+" ,
	 * i.e. at least one character from the set {a-z,A-Z, 0-9} (=only letters
	 * and digits are allowed)
	 */
	private String questionPattern = "(\\w)+";

	/**
	 * this mode compares just the raw {@link String} values of the Mturk XML
	 * response.
	 */
	public static final int RAW_ANSWER_IDENTICAL = 0;

	/**
	 * this mode parses the answer, and compares only the answer values of
	 * question identifiers which do not contain the word "comment"
	 */
	public static final int IGNORE_COMMENT_FIELDS = 1;

	/**
	 * this mode parses the answer, and compares only the answer values of
	 * question identifiers which does not match a certain pattern as specified
	 * by questionPattern. Default pattern is "(\\w)+" , i.e. at least one
	 * character from the set {a-z,A-Z, 0-9} (=only letters and digits are
	 * allowed)
	 */
	public static final int IGNORE_NON_PATTERN_ANSWERS = 2;

	/**
	 * contains the parsed answer data as key-value set. key is always the
	 * content of the element QuestionIdentifier, value is a concatenated list
	 * of all FreeText, SelctionIdentifier, OtherSelectionText,
	 * UploadedFileSizeInBytes and UploadedFileKey elements.
	 */
	private HashMap<String, String> parsedAnswers;

	/**
	 * @param rawAnswer
	 *            the value returned by assignmentInstance.getAnswer()
	 * @param comparisonMode
	 *            the mode (see static fields). if an unsupported value is
	 *            provided the class defaults to RAW_ANSWER_IDENTICAL
	 */
	public BasicAssignmentAnswer(String rawAnswer, Integer comparisonMode) {
		super(rawAnswer);
		if (comparisonMode == null)
			throw new RuntimeException(
					"The parameter comparisonMode may not be null!");
		switch (comparisonMode) {
		case IGNORE_COMMENT_FIELDS: {
			this.mode = IGNORE_COMMENT_FIELDS;
			this.parsedAnswers = parseAnswer(rawAnswer);
			break;
		}
		case IGNORE_NON_PATTERN_ANSWERS: {
			this.mode = IGNORE_NON_PATTERN_ANSWERS;
			this.parsedAnswers = parseAnswer(rawAnswer);
			break;
		}
		default:
			this.mode = RAW_ANSWER_IDENTICAL;
		}
	}

	@Override
	public boolean equals(AbstractAssignmentAnswer other) {
		switch (mode) {
		case IGNORE_NON_PATTERN_ANSWERS:
			return equalsForIGNORE_NON_PATTERN_ANSWERS(other);
		case IGNORE_COMMENT_FIELDS:
			return equalsForIGNORE_COMMENT_FIELDS(other);
		default:
			return rawAnswer.equals(other.rawAnswer);
		}

	}

	/**
	 * @param other
	 * @return
	 */
	private boolean equalsForIGNORE_COMMENT_FIELDS(
			AbstractAssignmentAnswer other) {
		if (parsedAnswers == null)
			return false;
		HashMap<String, String> thisone = parsedAnswers, otherone = null;
		if (other instanceof BasicAssignmentAnswer) {
			BasicAssignmentAnswer b = (BasicAssignmentAnswer) other;
			if (b.parsedAnswers == null)
				b.parsedAnswers = parseAnswer(b.rawAnswer);
			if (b.parsedAnswers == null)
				return false;
			otherone = b.parsedAnswers;
		} else {
			otherone = parseAnswer(other.rawAnswer);
			if (otherone == null)
				return false;
		}
		// compare all entries
		if (thisone.size() != otherone.size())
			return false;
		String val1, val2;
		for (String key : thisone.keySet()) {
			if (key == null)
				throw new RuntimeException("Keys must not be null!\n" + thisone
						+ "\n\n" + otherone);
			if (key.contains("comment"))
				continue;
			val1 = thisone.get(key);
			val2 = otherone.get(key);
			if (val1 == null || val2 == null)
				return false;
			if (!(val1.equals(val2)))
				return false;
		}
		// finally: no reason to return false
		return true;
	}

	/**
	 * @param other
	 * @return
	 */
	private boolean equalsForIGNORE_NON_PATTERN_ANSWERS(
			AbstractAssignmentAnswer other) {
		if (parsedAnswers == null)
			return false;
		HashMap<String, String> thisone = parsedAnswers, otherone = null;
		if (other instanceof BasicAssignmentAnswer) {
			BasicAssignmentAnswer b = (BasicAssignmentAnswer) other;
			if (b.parsedAnswers == null)
				b.parsedAnswers = parseAnswer(b.rawAnswer);
			if (b.parsedAnswers == null)
				return false;
			otherone = b.parsedAnswers;
		} else {
			otherone = parseAnswer(other.rawAnswer);
			if (otherone == null)
				return false;
		}
		// compare all entries
		if (thisone.size() != otherone.size())
			return false;
		String val1, val2;
		for (String key : thisone.keySet()) {
			if (key == null)
				throw new RuntimeException("Keys must not be null!\n" + thisone
						+ "\n\n" + otherone);
			if (!key.matches(questionPattern))
				continue;
			val1 = thisone.get(key);
			val2 = otherone.get(key);
			if (val1 == null || val2 == null)
				return false;
			if (!(val1.equals(val2)))
				return false;
		}
		// finally: no reason to return false
		return true;
	}

	/**
	 * @return the questionPattern
	 */
	public String getQuestionPattern() {
		return this.questionPattern;
	}

	/**
	 * @param questionPattern
	 *            the questionPattern to set
	 */
	public void setQuestionPattern(String questionPattern) {
		this.questionPattern = questionPattern;
	}

	/**
	 * @return the mode
	 */
	public int getMode() {
		return this.mode;
	}

}
